package galaxy

import (
	"context"
	"errors"
	"os"

	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/daemon"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/lock_file"
)

type ExtCommand interface {
	Execute() error
	BindEnv(env def.Env)
}
type PreHandler func(ctx context.Context, logger def.Logger, config *Config) ([]interface{}, error)

const (
	VERSION = ""
)

var (
	exit    = make(chan struct{})
	command ExtCommand
	rl      *rootLogger
	config  *Config
	flags   *Flags

	logger     def.Logger
	ctx        context.Context
	cancelFunc context.CancelFunc

	daemonInfo *daemon.Daemon
	needStart  = true
)

func init() {
	rl = &rootLogger{}
	rl.init()
	logger = rl.Logger()

	config = &Config{}

	flags = &Flags{}
	ctx, cancelFunc = context.WithCancel(context.Background())
}

func GetLogger() def.Logger {
	return rl.Logger()
}

func AttachCommand(cmd ExtCommand) {
	command = cmd
	command.BindEnv(flags)
}

func SetInfo(subject string, description string, version string) {
	flags.subject = subject
	flags.description = description
	flags.versionInfo = version
}

func Init(fns ...func() error) error {
	if err := RegisterInterface(logger); err != nil {
		return err
	}
	if err := RegisterInterface(config); err != nil {
		return err
	}
	if command == nil {
		flags.init()
		if err := flags.execute(); err != nil {
			return err
		}
		if fns != nil {
			for k := range fns {
				if err := fns[k](); err != nil {
					return err
				}
			}
		}
	} else {
		if err := command.Execute(); err != nil {
			return err
		}
	}
	return nil
}

func PreStart(preHandler PreHandler) error {
	var err error
	needStart = true
	if flags.Daemon && !flags.Test {
		daemonInfo = new(daemon.Daemon)
		daemonInfo.WorkDir, err = os.Getwd()
		if err != nil {
			return err
		}
		daemonInfo.PidFileName = flags.PidFile

		child, err := daemonInfo.Reborn()
		if err != nil {
			if err == lock_file.ErrWouldBlock {
				return errors.New("Daemon pid file exist, need Stop Daemon or kill Daemon")
			}
			return err
		}
		if child != nil {
			logger.Debugln("[ Galaxy ] Start Daemon")
			needStart = false
			return nil
		}
	}

	if flags.LogName != "" {
		if err := rl.bindFile(flags.LogName); err != nil {
			return err
		}
	}

	if preHandler != nil {
		result, err := preHandler(ctx, logger, config)
		if err != nil {
			return err
		}
		for key := range result {
			if err := RegisterInterface(result[key]); err != nil {
				return err
			}
		}
	}
	if err := registerCoreServer(); err != nil {
		return err
	}
	logger.Debugln("[ Galaxy ] Pre Start success")

	return nil
}

func Start(fns ...func() error) error {
	if !needStart {
		return nil
	}
	if flags.PidFile != "" {
		if pid, err := utils.CheckPid(flags.PidFile); err != nil {
			return err
		} else if pid > 0 {
			logger.Warnf("[ Galaxy ] Server already exist pid: %v", pid)
			return nil
		}
	}
	for k := range fns {
		if err := fns[k](); err != nil {
			return err
		}
	}
	if err := bindServer(); err != nil {
		return err
	}

	if flags.Test {
		logger.Infof("[ Galaxy ] Server start test is successful")
		return nil
	}

	logger.Infof("[ Galaxy ] Start...")

	if daemonInfo == nil && flags.PidFile != "" {
		if _, err := utils.MakePid(flags.PidFile); err != nil {
			cancelFunc()
			return err
		}
	}

	if err := startServer(ctx); err != nil {
		logger.Fatalf("[ Galaxy ] StartServer: %s", err)
		cancelFunc()
		return err
	}
	go utils.CloseHandler(func(s os.Signal, arg interface{}) error {
		logger.Infof("[ Galaxy ] System Signal Stop")
		exit <- struct{}{}
		return nil
	})
	<-exit
	cancelFunc()
	if daemonInfo != nil {
		_ = daemonInfo.Release()
	} else if flags.PidFile != "" {
		_ = utils.RmPid(flags.PidFile)
	}
	logger.Infof("[ Galaxy ] Stop...")
	if err := stopServer(); err != nil {
		return err
	}
	logger.Infof("[ Galaxy ] Stop Finish")
	rl.exit()
	return nil
}

func Exit() {
	if !needStart {
		return
	}
	logger.Infof("[ Galaxy ] User Request Stop")
	exit <- struct{}{}
}
